/*
 * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 */

package java.nio.channels.spi;

import java.io.IOException;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.util.HashSet;
import java.util.Set;
import sun.nio.ch.Interruptible;
import java.util.concurrent.atomic.AtomicBoolean;


/**
 * 选择器的基实现类。
 *
 * <p> 这个类封装了实现选择操作中断所需的低级机制。
 * 一个具体的选择器类必须在调用I/O操作之前和之后分别调用begin和end方法，这样调用的I/O操作肯定会阻塞。
 * 为了确保end方法总是被调用，这些方法应该在try…finally块:
 *
 * <blockquote><pre>
 * try {
 *     begin();
 *     // Perform blocking I/O operation here
 *     ...
 * } finally {
 *     end();
 * }</pre></blockquote>
 *
 * <p> 该类还定义了维护选择器的取消键集和从通道的键集中移除键的方法，
 * 并声明了抽象的register方法，该方法由可选通道的register方法调用，以执行注册通道的实际工作。</p>
 *
 *
 * @author Mark Reinhold
 * @author JSR-51 Expert Group
 * @since 1.4
 */

public abstract class AbstractSelector
    extends Selector
{

    private AtomicBoolean selectorOpen = new AtomicBoolean(true);

    // 创建这个选择器的提供者
    private final SelectorProvider provider;

    /**
     * Initializes a new instance of this class.
     *
     * @param  provider
     *         The provider that created this selector
     */
    protected AbstractSelector(SelectorProvider provider) {
        this.provider = provider;
    }

    private final Set<SelectionKey> cancelledKeys = new HashSet<SelectionKey>();

    void cancel(SelectionKey k) {                       // package-private
        synchronized (cancelledKeys) {
            cancelledKeys.add(k);
        }
    }

    /**
     * 关闭这个选择器。
     *
     * <p> 如果选择器已经关闭，那么这个方法将立即返回。
     * 否则，它将选择器标记为关闭，然后调用implCloseSelector方法来完成关闭操作。</p>
     *
     * @throws  IOException
     *          If an I/O error occurs
     */
    public final void close() throws IOException {
        boolean open = selectorOpen.getAndSet(false);
        if (!open)
            return;
        implCloseSelector();
    }

    /**
     * 关闭这个选择器。
     *
     * <p> 该方法由close方法调用，以便执行关闭选择器的实际工作。
     * 此方法只在选择器尚未关闭时被调用，并且不会被调用多次。
     *
     * <p> 这个方法的实现必须安排在选择器的选择操作中阻塞的任何其他线程立即返回，就像调用wakeup方法一样。 </p>
     *
     * @throws  IOException
     *          If an I/O error occurs while closing the selector
     */
    protected abstract void implCloseSelector() throws IOException;

    public final boolean isOpen() {
        return selectorOpen.get();
    }

    /**
     * 返回创建此通道的提供程序。
     *
     * @return  The provider that created this channel
     */
    public final SelectorProvider provider() {
        return provider;
    }

    /**
     * 检索这个选择器的取消键集。
     *
     * <p>该集合应该只在同步时使用。</p>
     *
     * @return  The cancelled-key set
     */
    protected final Set<SelectionKey> cancelledKeys() {
        return cancelledKeys;
    }

    /**
     * 用这个选择器注册给定的通道。
     *
     * <p> 该方法由通道的register方法调用，以执行向该选择器注册通道的实际工作。</p>
     *
     * @param  ch
     *         The channel to be registered
     *
     * @param  ops
     *         The initial interest set, which must be valid
     *
     * @param  att
     *         The initial attachment for the resulting key
     *
     * @return  A new key representing the registration of the given channel
     *          with this selector
     */
    protected abstract SelectionKey register(AbstractSelectableChannel ch,
                                             int ops, Object att);

    /**
     * 从其通道的键集中删除给定的键。
     *
     * <p> 该方法必须由选择器为其所在的每个通道调用注销。 </p>
     *
     * @param  key
     *         The selection key to be removed
     */
    protected final void deregister(AbstractSelectionKey key) {
        ((AbstractSelectableChannel)key.channel()).removeKey(key);
    }


    // -- Interruption machinery --

    private Interruptible interruptor = null;

    /**
     * 标志一个可能无限期阻塞的I/O操作的开始。
     *
     * <p> 这个方法应该与end方法一起调用，使用try…finally阻塞如上所示，为了实现这个选择器的中断。
     *
     * <p> 调用这个方法安排了选择器的wakeup方法被调用，如果线程的中断方法被调用，而线程阻塞在一个I/O操作上的选择器。</p>
     */
    protected final void begin() {
        if (interruptor == null) {
            interruptor = new Interruptible() {
                    public void interrupt(Thread ignore) {
                        AbstractSelector.this.wakeup();
                    }};
        }
        AbstractInterruptibleChannel.blockedOn(interruptor);
        Thread me = Thread.currentThread();
        if (me.isInterrupted())
            interruptor.interrupt(me);
    }

    /**
     * 标志一个可能无限期阻塞的I/O操作的结束。
     *
     * <p>这个方法应该与begin方法一起调用，使用try…finally阻塞如上所示，为了实现这个选择器的中断。</p>
     */
    protected final void end() {
        AbstractInterruptibleChannel.blockedOn(null);
    }

}
